﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using TNet;
using UnityEngine;

[ExecuteInEditMode]
public class TNAutoSync : TNBehaviour
{
    public System.Collections.Generic.List<SavedEntry> entries = new System.Collections.Generic.List<SavedEntry>();
    public bool isImportant;
    public bool isSavedOnServer = true;
    private object[] mCached;
    private TNet.List<ExtendedEntry> mList = new TNet.List<ExtendedEntry>();
    public bool onlyOwnerCanSync = true;
    public int updatesPerSecond = 10;

    private void Awake()
    {
        int num = 0;
        int count = this.entries.Count;
        while (num < count)
        {
            SavedEntry entry = this.entries[num];
            if ((entry.target != null) && !string.IsNullOrEmpty(entry.propertyName))
            {
                FieldInfo field = entry.target.GetType().GetField(entry.propertyName, BindingFlags.Public | BindingFlags.Instance);
                if (field != null)
                {
                    ExtendedEntry item = new ExtendedEntry {
                        target = entry.target,
                        field = field,
                        lastValue = field.GetValue(entry.target)
                    };
                    this.mList.Add(item);
                }
                else
                {
                    PropertyInfo property = entry.target.GetType().GetProperty(entry.propertyName, BindingFlags.Public | BindingFlags.Instance);
                    if (property != null)
                    {
                        ExtendedEntry entry3 = new ExtendedEntry {
                            target = entry.target,
                            property = property,
                            lastValue = property.GetValue(entry.target, null)
                        };
                        this.mList.Add(entry3);
                    }
                    else
                    {
                        UnityEngine.Debug.LogError(string.Concat(new object[] { "Unable to find property: '", entry.propertyName, "' on ", entry.target.GetType() }));
                    }
                }
            }
            num++;
        }
        if (this.mList.size > 0)
        {
            if (this.updatesPerSecond > 0)
            {
                base.StartCoroutine(this.PeriodicSync());
            }
        }
        else
        {
            UnityEngine.Debug.LogWarning("Nothing to sync", this);
            base.enabled = false;
        }
    }

    private bool Cache()
    {
        bool flag = false;
        bool flag2 = false;
        if (this.mCached == null)
        {
            flag = true;
            this.mCached = new object[this.mList.size];
        }
        for (int i = 0; i < this.mList.size; i++)
        {
            object obj2;
            ExtendedEntry entry = this.mList[i];
            obj2 = (entry.field == null) ? (obj2 = entry.property.GetValue(entry.target, null)) : (obj2 = entry.field.GetValue(entry.target));
            if (!obj2.Equals(entry.lastValue))
            {
                flag2 = true;
            }
            if (flag || flag2)
            {
                entry.lastValue = obj2;
                this.mCached[i] = obj2;
            }
        }
        return flag2;
    }

    private void OnNetworkPlayerJoin(Player p)
    {
        if (((this.mList.size != 0) && !this.isSavedOnServer) && TNManager.isHosting)
        {
            if (this.Cache())
            {
                this.Sync();
            }
            else
            {
                base.tno.Send((byte) 0xff, p, this.mCached);
            }
        }
    }

    [TNet.RFC(0xff)]
    private void OnSync(object[] val)
    {
        if (base.enabled)
        {
            for (int i = 0; i < this.mList.size; i++)
            {
                ExtendedEntry entry = this.mList[i];
                entry.lastValue = val[i];
                if (entry.field != null)
                {
                    entry.field.SetValue(entry.target, entry.lastValue);
                }
                else
                {
                    entry.property.SetValue(entry.target, entry.lastValue, null);
                }
            }
        }
    }

    [DebuggerHidden]
    private IEnumerator PeriodicSync()
    {
        return new <PeriodicSync>c__Iterator67 { <>f__this = this };
    }

    public void Sync()
    {
        if (TNManager.isInChannel && (this.mList.size != 0))
        {
            if (this.isImportant)
            {
                base.tno.Send((byte) 0xff, !this.isSavedOnServer ? Target.Others : Target.OthersSaved, this.mCached);
            }
            else
            {
                base.tno.SendQuickly((byte) 0xff, !this.isSavedOnServer ? Target.Others : Target.OthersSaved, this.mCached);
            }
        }
    }

    [CompilerGenerated]
    private sealed class <PeriodicSync>c__Iterator67 : IEnumerator, IDisposable, IEnumerator<object>
    {
        internal object $current;
        internal int $PC;
        internal TNAutoSync <>f__this;

        [DebuggerHidden]
        public void Dispose()
        {
            this.$PC = -1;
        }

        public bool MoveNext()
        {
            uint num = (uint) this.$PC;
            this.$PC = -1;
            switch (num)
            {
                case 0:
                case 1:
                case 2:
                    if (!TNManager.isInChannel || (this.<>f__this.updatesPerSecond <= 0))
                    {
                        this.$current = new WaitForSeconds(0.1f);
                        this.$PC = 2;
                    }
                    else
                    {
                        if (((this.<>f__this.mList.size != 0) && (!this.<>f__this.onlyOwnerCanSync || this.<>f__this.tno.isMine)) && this.<>f__this.Cache())
                        {
                            this.<>f__this.Sync();
                        }
                        this.$current = new WaitForSeconds(1f / ((float) this.<>f__this.updatesPerSecond));
                        this.$PC = 1;
                    }
                    return true;

                default:
                    break;
                    this.$PC = -1;
                    break;
            }
            return false;
        }

        [DebuggerHidden]
        public void Reset()
        {
            throw new NotSupportedException();
        }

        object IEnumerator<object>.Current
        {
            [DebuggerHidden]
            get
            {
                return this.$current;
            }
        }

        object IEnumerator.Current
        {
            [DebuggerHidden]
            get
            {
                return this.$current;
            }
        }
    }

    private class ExtendedEntry : TNAutoSync.SavedEntry
    {
        public FieldInfo field;
        public object lastValue;
        public PropertyInfo property;
    }

    [Serializable]
    public class SavedEntry
    {
        public string propertyName;
        public Component target;
    }
}

